package com.gisquest.cloud.service.impl;

import com.alibaba.fastjson.JSON;
import com.gisquest.cloud.common.cache.RedisService;
import com.gisquest.cloud.common.exception.BizException;
import com.gisquest.cloud.common.utils.DateUtil;
import com.gisquest.cloud.entity.po.BizLogEntity;
import com.gisquest.cloud.entity.po.SysLogEntity;
import com.gisquest.cloud.entity.vo.RequestEntity;
import com.gisquest.cloud.entity.vo.RestResult;
import com.gisquest.cloud.entity.vo.SysLogVO;
import com.gisquest.cloud.service.EsService;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * @Author guowei
 * @Date 2020/6/19 10:38
 * @Description:直接调用es
 **/
@Service
public class EsServiceImpl implements EsService {

  @Value("${es.hostname}")
  private  String esHostName;
  @Value("${es.port}")
  private  int port;
  @Value("${es.index}")
  private  String esIndex;
  @Value("${es.biztype}")
  private  String bizType;
  @Value("${es.systype}")
  private  String sysType;
  @Value("${rs.sys.key}")
  private String sysLogKey;
  @Value("${rs.biz.key}")
  private String bizLogKey;
  @Value("${es.clusterName}")
  private String esClusterName;
  @Autowired
  RedisService redisService;

  private static TransportClient client = null;

  @Override
  public RestResult<?> getBizLogFromEs(RequestEntity requestEntity) {

    //高版本使用RestHighLevelClient
//    RestClientBuilder lowLevelRestClient = RestClient.builder(new HttpHost(esHostName, port, "http"));
//    RestHighLevelClient client = new RestHighLevelClient(lowLevelRestClient);
    //指定索引
//    SearchRequest searchRequest = new SearchRequest(esIndex);
    //指定type
//    searchRequest.types(bizType);
    //构造一个默认配置的对象
//    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

    //低版本使用TransportClient
    TransportClient client = getClient(esHostName,port,esClusterName);

    //匹配查询条件
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    if (!StringUtils.isEmpty(requestEntity.getYwh())) {
      boolQuery.must(QueryBuilders.wildcardQuery("ywh", "*" + requestEntity.getYwh() + "*"));
    }
    if (!StringUtils.isEmpty(requestEntity.getOptId())) {
      boolQuery.must(QueryBuilders.wildcardQuery("optId", "*" + requestEntity.getOptId() + "*"));
    }
    if (!StringUtils.isEmpty(requestEntity.getRequestId())) {
      boolQuery.must(QueryBuilders.wildcardQuery("requestId", requestEntity.getRequestId()));
    }
    if (!StringUtils.isEmpty(requestEntity.getMicroName())) {
      boolQuery.must(QueryBuilders.wildcardQuery("microName", requestEntity.getMicroName()));
    }
    if (!StringUtils.isEmpty(requestEntity.getServiceKind())) {
      boolQuery.must(QueryBuilders.wildcardQuery("serviceKind", requestEntity.getServiceKind()));
    }
    if (!StringUtils.isEmpty(requestEntity.getMethodName())) {
      boolQuery.must(QueryBuilders.wildcardQuery("methodName", requestEntity.getMethodName()));
    }
    if (!StringUtils.isEmpty(requestEntity.getRequestUrl())) {
      boolQuery.must(QueryBuilders.wildcardQuery("requestUrl", requestEntity.getRequestUrl()));
    }
    if (!StringUtils.isEmpty(requestEntity.getQuerystring())) {
      boolQuery.must(QueryBuilders.wildcardQuery("querystring", "*" + requestEntity.getQuerystring() + "*"));
    }
    if (!StringUtils.isEmpty(requestEntity.getOptKind())) {
      boolQuery.must(QueryBuilders.wildcardQuery("optKind", requestEntity.getOptKind()));
    }

    String startTime = requestEntity.getRequestStartTime();
    String endTime = requestEntity.getRequestEndTime();
    int size = requestEntity.getPageSize();

    //默认获取当天的数据
    if (StringUtils.isEmpty(startTime) && StringUtils.isEmpty(endTime)){
      startTime = DateUtil.getStringFromDate(new Date()) + " 00:00:00.000";
      endTime = DateUtil.getStringFromDate(new Date()) + " 23:59:59.000";
    }

    if (StringUtils.isEmpty(startTime) && !StringUtils.isEmpty(endTime)){
      //设置时间区间
      boolQuery.must(QueryBuilders.rangeQuery("requestTime").
          to(DateUtil.getDateFromStr(endTime).getTime()));
    }else if (!StringUtils.isEmpty(startTime) && StringUtils.isEmpty(endTime)){
      boolQuery.must(QueryBuilders.rangeQuery("requestTime").
          from(DateUtil.getDateFromStr(startTime).getTime()));
    }else {
      boolQuery.must(QueryBuilders.rangeQuery("requestTime").
          from(DateUtil.getDateFromStr(startTime).getTime()).
          to(DateUtil.getDateFromStr(endTime).getTime()));
    }


    //低版本es
    SearchRequestBuilder srb = client.prepareSearch(esIndex);
    if (srb == null) {
      throw new IllegalArgumentException(String.format("es查询索引(%s)指定不正确!", esIndex));
    }
    SearchResponse searchResponse = client.prepareSearch(esIndex).setTypes(bizType)
        .setQuery(boolQuery)
        .setFrom(requestEntity.getPageNum() - 1)
        .setSize(size)
        .addSort("requestTime", "asc".equalsIgnoreCase(requestEntity.getOrder()) ? SortOrder.ASC : SortOrder.DESC)
        .setExplain(true)
        .execute()
        .actionGet();

    //高版本es
//    sourceBuilder.query(boolQuery);
    //从第几页数据开始
//    sourceBuilder.from(requestEntity.getPageNum() - 1);
    //每一页的条数
//    sourceBuilder.size(size);
    //设置超时时间
//    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    //日志排序功能因太耗内存默认是禁用的
//    sourceBuilder.sort("requestTime", "asc".equalsIgnoreCase(requestEntity.getOrder()) ? SortOrder.ASC : SortOrder.DESC);
//    searchRequest.source(sourceBuilder);

    List<Map<String, Object>> list = new ArrayList<>();
    HashMap<String, Object> resultMap = new HashMap();
    resultMap.put("size", size);
    resultMap.put("num", requestEntity.getPageNum());
    try {
      long count = searchResponse.getHits().totalHits;
      SearchHit[] hits = searchResponse.getHits().getHits();
      for (SearchHit hit : hits) {
        Map<String, Object> map = hit.getSourceAsMap();
        Map<String, Object> mapData = new HashMap<>();
        for (Map.Entry<String, Object> m : map.entrySet()) {
          if ("requestTime".equals(m.getKey())){
            String value = m.getValue() + "";
            if (!value.contains("-") && !value.contains(" ")){
              mapData.put(m.getKey(), DateUtil.getDateTime(Long.valueOf(value)));
            }
          }else if (!("@timestamp".equals(m.getKey()) || "type".equals(m.getKey()) || "@version".equals(m.getKey()))){
            mapData.put(m.getKey(), m.getValue());
          }
        }
        list.add(mapData);
      }

      int items = (int) (count%size);
      int pages;
      if (count <= size){
        pages = 1;
      } else if (items == 0) {
        pages = items;
      }else {
        pages = items + 1;
      }

      resultMap.put("data", list);
      resultMap.put("total", count);
      resultMap.put("size", size);
      resultMap.put("pages",pages);
      resultMap.put("num", requestEntity.getPageNum());
    } catch (Exception e) {
      e.printStackTrace();
    }

    return new RestResult<>().makeSuccessResult(resultMap);
  }


  @Override
  public RestResult<?> getSysLogFromEs(SysLogVO sysLogVO) {
    //高版本
//    RestClientBuilder lowLevelRestClient = RestClient.builder(new HttpHost(esHostName, port, "http"));
//    RestHighLevelClient client = new RestHighLevelClient(lowLevelRestClient);

    //指定索引
//    SearchRequest searchRequest = new SearchRequest(esIndex);
    //指定type
//    searchRequest.types(sysType);
    //构造一个默认配置的对象
//    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();


    //低版本使用TransportClient
    TransportClient client = getClient(esHostName,port,esClusterName);

    //匹配查询条件
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    if (!StringUtils.isEmpty(sysLogVO.getRequestId())) {
      boolQuery.must(QueryBuilders.wildcardQuery("requestId", sysLogVO.getRequestId()));
    }
    if (!StringUtils.isEmpty(sysLogVO.getClassName())) {
      boolQuery.must(QueryBuilders.wildcardQuery("className", sysLogVO.getClassName()));
    }

    String startTime = sysLogVO.getStartTime();
    String endTime = sysLogVO.getEndTime();

    //默认获取当天的数据
    if (StringUtils.isEmpty(startTime) && StringUtils.isEmpty(endTime)){
      startTime = DateUtil.getStringFromDate(new Date()) + " 00:00:00.000";
      endTime = DateUtil.getStringFromDate(new Date()) + " 23:59:59.000";
    }

    if (StringUtils.isEmpty(startTime) && !StringUtils.isEmpty(endTime)){
      //设置时间区间
      boolQuery.must(QueryBuilders.rangeQuery("createTime").
          to(DateUtil.getDateFromStr(endTime).getTime()));
    }else if (!StringUtils.isEmpty(startTime) && StringUtils.isEmpty(endTime)){
      boolQuery.must(QueryBuilders.rangeQuery("createTime").
          from(DateUtil.getDateFromStr(startTime).getTime()));
    }else {
      boolQuery.must(QueryBuilders.rangeQuery("createTime").
          from(DateUtil.getDateFromStr(startTime).getTime()).
          to(DateUtil.getDateFromStr(endTime).getTime()));
    }

    int size = sysLogVO.getPageSize();


    //高版本
//    sourceBuilder.query(boolQuery);
//    //从第几页数据开始(默认从第0页开始)
//    sourceBuilder.from(sysLogVO.getPageNum() - 1);
//    //每一页的条数
//    sourceBuilder.size(size);
//    //设置超时时间
//    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//    //日志排序功能因太耗内存默认是禁用的
//    sourceBuilder.sort("createTime", "asc".equalsIgnoreCase(sysLogVO.getOrder()) ? SortOrder.ASC : SortOrder.DESC);
//    searchRequest.source(sourceBuilder);

    //低版本es
    SearchRequestBuilder srb = client.prepareSearch(esIndex);
    if (srb == null) {
      throw new IllegalArgumentException(String.format("es查询索引(%s)指定不正确!", esIndex));
    }
    SearchResponse searchResponse = client.prepareSearch(esIndex).setTypes(sysType)
        .setQuery(boolQuery)
        .setFrom(sysLogVO.getPageNum() - 1)
        .setSize(size)
        .addSort("createTime", "asc".equalsIgnoreCase(sysLogVO.getOrder()) ? SortOrder.ASC : SortOrder.DESC)
        .setExplain(true)
        .execute()
        .actionGet();

    List<Map<String, Object>> list = new ArrayList<>();
    HashMap<String, Object> resultMap = new HashMap();
    resultMap.put("size", size);
    resultMap.put("num", sysLogVO.getPageNum());
    try {
      long count = searchResponse.getHits().totalHits;
      SearchHit[] hits = searchResponse.getHits().getHits();
      for (SearchHit hit : hits) {
        Map<String, Object> map = hit.getSourceAsMap();
        Map<String, Object> mapData = new HashMap<>();
        for (Map.Entry<String, Object> m : map.entrySet()) {
          if ("createTime".equals(m.getKey())){
            String value = m.getValue() + "";
            if (!value.contains("-") && !value.contains(" ")){
              mapData.put(m.getKey(), DateUtil.getDateTime(Long.valueOf(value)));
            }
          }else if (!("@timestamp".equals(m.getKey()) || "type".equals(m.getKey()) || "@version".equals(m.getKey()))){
            mapData.put(m.getKey(), m.getValue());
          }
        }
        list.add(mapData);
      }

      int items = (int) (count%size);
      int pages;
      if (count <= size){
        pages = 1;
      } else if (items == 0) {
        pages = items;
      }else {
        pages = items + 1;
      }

      resultMap.put("data", list);
      resultMap.put("pages",pages);
      resultMap.put("total", count);
    } catch (Exception e) {
      e.printStackTrace();
    }

    return new RestResult<>().makeSuccessResult(resultMap);
  }


  @Override
  public RestResult<?> saveBizLogToEs(BizLogEntity bizLogEntity) {
    //高版本
//    RestClientBuilder lowLevelRestClient = RestClient.builder(new HttpHost(esHostName, port, "http"));
//    RestHighLevelClient client = new RestHighLevelClient(lowLevelRestClient);

    //低版本使用TransportClient
    TransportClient client = getClient(esHostName,port,esClusterName);

    String jsonStr = getBizLogJsonStr(bizLogEntity);
    //可以是map,jsonstring(需要指定类型XContentType.JSON),XContentBuilder或直接是k-v
    Map<String, Object> map = JSON.parseObject(jsonStr, Map.class);
    //不指定id时es会自动生成UUID相当于数据库的主键
    //注意当数据的v是空的时候,会自动过滤掉字段
    IndexRequest indexRequest = new IndexRequest(esIndex, bizType).source(map);
    try {
      //高版本
//      client.index(indexRequest, RequestOptions.DEFAULT);

      //低版本
      client.index(indexRequest);

    } catch (Exception e) {
      e.printStackTrace();
    }
    return new RestResult<>().makeSuccessResult();
  }


  /**
   * 通过传过来的对象获取存储对象的json字符串(业务日志)
   * @param bizLogEntity
   * @return
   */
  public String getBizLogJsonStr(BizLogEntity bizLogEntity){
    if (StringUtils.isEmpty(bizLogEntity.getAppId())) {
      throw new BizException("appId不能为空");
    }
    if (StringUtils.isEmpty(bizLogEntity.getUserId()) || StringUtils.isEmpty(bizLogEntity.getUserName())) {
      throw new BizException("userId和userName均不能为空");
    }
    if (StringUtils.isEmpty(bizLogEntity.getYwh()) && StringUtils.isEmpty(bizLogEntity.getOptId())) {
      throw new BizException("业务标识和操作标识不能同时为空");
    }
    if (StringUtils.isEmpty(bizLogEntity.getRequestId())) {
      throw new BizException("请传入请求标识");
    }
    if (StringUtils.isEmpty(bizLogEntity.getRequestUrl())) {
      throw new BizException("请传入请求路径");
    }
    bizLogEntity.setId(UUID.randomUUID().toString().replace("-", ""));
    bizLogEntity.setRequestTime(new Date().getTime());
    String jsonStr = JSON.toJSONString(bizLogEntity);
    return jsonStr;
  }


  @Override
  public RestResult<?> saveBizLogToRs(BizLogEntity bizLogEntity) {
    String jsonStr = getBizLogJsonStr(bizLogEntity);
    //redis配置的类型指定为list,推荐使用rpush(channel推荐使用publish)
    redisService.rpush(bizLogKey,jsonStr);
    return new RestResult<>().makeSuccessResult();
  }


  @Override
  public RestResult<?> saveSysLogToEs(SysLogVO sysLogVO) {
    //高版本
//    RestClientBuilder lowLevelRestClient = RestClient.builder(new HttpHost(esHostName, port, "http"));
//    RestHighLevelClient client = new RestHighLevelClient(lowLevelRestClient);

    //低版本使用TransportClient
    TransportClient client = getClient(esHostName,port,esClusterName);

    String jsonStr = getSysLogJsonStr(sysLogVO);
    Map<String, Object> map = JSON.parseObject(jsonStr, Map.class);
    IndexRequest indexRequest = new IndexRequest(esIndex, sysType).source(map);
    try {
      //高版本
//      client.index(indexRequest, RequestOptions.DEFAULT);

      //低版本
      client.index(indexRequest);

    } catch (Exception e) {
      e.printStackTrace();
    }
    return new RestResult<>().makeSuccessResult();
  }


  /**
   * 通过传过来的对象获取存储对象的json字符串(系统日志)
   * @param sysLogVO
   * @return
   */
  public String getSysLogJsonStr(SysLogVO sysLogVO){
    if (StringUtils.isEmpty(sysLogVO.getRequestId())) {
      throw new BizException("requestId不能为空");
    }
    if (StringUtils.isEmpty(sysLogVO.getMsg())) {
      throw new BizException("日志信息不能为空");
    }
    SysLogEntity sysLogEntity = new SysLogEntity();
    if (StringUtils.isEmpty(sysLogVO.getCreateTime())){
      //默认为当前时间
      sysLogEntity.setCreateTime(new Date().getTime());
    }else {
      sysLogEntity.setCreateTime(DateUtil.getDateFromStr(sysLogVO.getCreateTime()).getTime());
    }
    sysLogEntity.setClassName(sysLogVO.getClassName());
    sysLogEntity.setLevel(sysLogVO.getLevel());
    sysLogEntity.setMsg(sysLogVO.getMsg());
    sysLogEntity.setRequestId(sysLogVO.getRequestId());
    String jsonStr = JSON.toJSONString(sysLogEntity);
    return jsonStr;
  }


  @Override
  public RestResult<?> saveSysLogToRs(SysLogVO sysLogVO) {
    String jsonStr = getSysLogJsonStr(sysLogVO);
    //redis配置的类型指定为list,推荐使用rpush(channel推荐使用publish)
    redisService.rpush(sysLogKey,jsonStr);
    return new RestResult<>().makeSuccessResult();
  }

  public TransportClient getClient(String esHostName,int port,String esClusterName) {
    if (client == null) {
      try {
        InetSocketTransportAddress node = new InetSocketTransportAddress(
            InetAddress.getByName(esHostName),port);
        Settings settings = Settings.builder()
            .put("cluster.name", esClusterName)
            .build();
        client = new PreBuiltTransportClient(settings)
            .addTransportAddress(node);
      } catch (UnknownHostException e) {
        e.printStackTrace();
      }
    }
    return client;
  }
}
